这是qemu在网络实现的时候的一个指针错误,当重组大量的ipv4分段数据包时会触发错误,这还是大牛通过代码审计发现的,厉害啊。
漏洞细节
qemu的网络有两部分
1、为虚拟机提供的虚拟网卡(比如PCI网卡)
2、与网络接口控制器交互的网络后端(就是将网络数据包给到主机网络)
默认情况下,QEMU将为guest虚拟机创建SLiRP用户网络后端和适当的虚拟网卡(例如e1000 PCI网卡)
而本漏洞是在SLiRP中的数据包重组中出现的错误。
数据包重组那就需要了解ip分片,ip层的结构如下:
1 | 0 1 2 3 |
分片在Flags那里,主要是低3个bit
1 | Bit 0: 保留为, 必须为0 |
下面看下相关的结构体
1 | struct mbuf { |
mbuf是储存接收到的ip层的信息。有两个buffer,一个是m_dat ,另一个是m_ext,他是m_dat不足以储存的时候,通过在堆上分配内存解决不足的问题
在nat转换的时候,如果传入的数据包是分片的,则应在编辑和重新传输之前重新组装它们。 这个重组由ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
函数完成。 ip包含当前的IP数据包数据,fp是包含分段数据包的链表。
ip_reass执行以下操作:
1、如果第一个分配的fp为NULL,创建重组队列并将ip插入此队列。
2、检查片段是否与先前收到的片段重复,然后丢弃它。
3、如果收到所有分段的数据包,则重新组装它。 通过修改第一个数据包的头部为新的ip header。
1 | /* |
这个漏洞在于计算变量delta的值有问题,而上面这个代码假定了第一个分片数据包不会在external buffer中分配(m_ext)。
当分片数据在mbuf-> m_dat(q将在m_dat内)时,计算q-m-> dat有效(q是包含分片链表和数据包数据的结构)。
否则,如果分配了m_ext缓冲区,则q将位于external buffer ,那么delta的计算就是错误的。
q的数据结构是ipasfrag:就是有一个前向指针跟后向指针,以及包含了一个ip头
1 | /* |
我们可以调试看看q的某个时刻的状态是怎样的
1 | gdb-peda$ p *q |
可以看到确实是两个ipasfrag前后指针还有ip头信息
我们继续调试运行到下面地方
我们查看下数据结构,可以看到确实此时的q在m_ext的后面,而m_dat在老前面了,那么q - m->m_dat就是负数了
1 | gdb-peda$ p q |
简单的示意图如下:(忽略了分配了m_ext缓冲区,则q将位于external buffer)
1 | +------------------------------+ |
之后,新计算的指针q被转换为ip结构并且修改部分字段。由于错误地计算了delta,ip将指向不正确的位置,并且ip_src和ip_dst可用于将我们可控的数据写入错误计算的ip的位置。 如果计算出的ip位于没有映射的内存区域,这就会使qemu崩溃。
1 | slirp/src/ip_input.c:ip_reass |
参考
https://blog.bi0s.in/2019/08/24/Pwn/VM-Escape/2019-07-29-qemu-vm-escape-cve-2019-14378/